package controllers

import (
	"api_server/internal/services"
	"bytes"
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis"
	"golang.org/x/sync/errgroup"
	common "jihe.common"
	"jihe.common/oss"
	"jihe.common/protos/base_server"
	base_server_proto "jihe.common/protos/base_server/proto"
	"jihe.common/protos/calculate_server"
	"jihe.common/protos/calculate_server/proto"
	calculate_server_proto "jihe.common/protos/calculate_server/proto"
	"jihe.common/protos/order_server"
	order_server_proto "jihe.common/protos/order_server/proto"
	"jihe.common/resp"
	"jihe.common/tool"
	"strconv"
	"strings"
	"time"
)

type CalculateController struct {
	Redis common.Redis
}

type completeData struct {
	Vt        [][]float64 `json:"vt"`
	ObjUrl    string      `json:"obj_url"`
	NeedId    string      `json:"need_id"`
	Ratio     string      `json:"ratio"`
	ImgLine   string      `json:"img_line"`
	ImgFace   string      `json:"img_face"`
	CutpartId string      `json:"cutpart_id"`
	Code      string      `json:"code"`
	DxfUrl    string      `json:"dxf_url"`
}

func (s *CalculateController) DoCaculateCmd(c *gin.Context) {
	cmd := c.Query("cmd")
	if cmd == "get" {
		nextCutPart, err := calculate_server.CalculateGetNextCutpartByWait(c, &proto.CalculateNull{})
		if err != nil {
			resp.RespInternalErr(c, err.Error())
			return
		}
		//锁，防止并发获取到同一个任务
		result := s.Redis.Get().SetNX(s.Redis.GetCalculateQueueKey(nextCutPart.Id), 1, time.Hour*2).Val()
		if result {
			//直接算开始
			_, err := calculate_server.CalculateBegin(c, &proto.CalculateCutpartQueue{Id: nextCutPart.Id})
			if err != nil {
				resp.RespInternalErr(c, err.Error())
				return
			}
			resp.RespOk(c, nextCutPart)
			return
		}
		resp.RespOk(c)
		return
	} else if cmd == "begin" {
		//id := c.Query("id")
		//idInt64, _ := strconv.ParseInt(id, 10, 64)
		//if idInt64 == 0 {
		//	resp.RespParamErr(c)
		//	return
		//}
		//_, err := calculate_server.CalculateBegin(c, &proto.CalculateCutpartQueue{Id: idInt64})
		//if err != nil {
		//	resp.RespInternalErr(c, err.Error())
		//	return
		//}
		resp.RespOk(c)
		return
	} else if cmd == "complete" {
		complete := completeData{}
		err := c.BindJSON(&complete)
		if err != nil {
			resp.RespParamErr(c)
			return
		}
		id := c.Query("id")
		idInt64, _ := strconv.ParseInt(id, 10, 64)
		if idInt64 == 0 {
			resp.RespParamErr(c)
			return
		}
		go func(pidInt64 int64, pcompleteData completeData) {
			_, err = calculate_server.CalculateUpdateCompleteById(c, &proto.CalculateCutpartQueue{Id: pidInt64})
			if err != nil {
				common.Loger.Error("%v", err)
				return
			}
			pcompleteData.ObjUrl, err = tool.MakeAfterObj(pcompleteData.ObjUrl, pcompleteData.Vt)
			if err != nil {
				common.Loger.Error("%v", err)
				return
			}
			//计算变化后dxf_info
			getDxfInfoReq := base_server.DxfInfoReq{}
			getDxfInfoReq.CpName = pcompleteData.Code
			getDxfInfoReq.DxfPath = strings.Replace(pcompleteData.DxfUrl, "files.dizanz", "jihe-file.oss-cn-beijing-internal.aliyuncs", -1)
			getDxfInfoReq.ResourcePath = pcompleteData.ObjUrl
			getDxfInfoReqByte, _ := json.Marshal(getDxfInfoReq)
			dxfInfoRet, err := calculate_server.GetDxfInfo(context.Background(), &calculate_server_proto.GetDxfInfoReq{
				Base64: base64.StdEncoding.EncodeToString(getDxfInfoReqByte),
			})
			if err != nil {
				common.Loger.Error("%v", err)
				return
			}
			dxfInfo := base_server.DxfInfo{}
			err = json.Unmarshal([]byte(dxfInfoRet.Stdout), &dxfInfo)
			if err != nil {
				common.Loger.Error("%v", err)
				return
			}
			bucket, err := oss.GetClient().Bucket("jihe-file")
			if err != nil {
				common.Loger.Error("%v", err)
				return
			}
			fileName := fmt.Sprintf("%s/%d.json", time.Now().Format("2006-01-02"), tool.GetSnowflakeId())
			err = bucket.PutObject(fileName, strings.NewReader(dxfInfoRet.Stdout))
			if err != nil {
				common.Loger.Error("%v", err)
				return
			}
			//写入base_server成功裁片
			_, err = base_server.BaseCreateNeedCutpart(c, &base_server_proto.BaseNeedCutpart{
				NeedId:          pcompleteData.NeedId,
				Ratio:           pcompleteData.Ratio,
				ImgLine:         pcompleteData.ImgLine,
				ImgFace:         pcompleteData.ImgFace,
				CutpartId:       pcompleteData.CutpartId,
				AfterObj:        pcompleteData.ObjUrl,
				Code:            pcompleteData.Code,
				AfterDxfInfoUrl: "https://files.dizanz.com/" + fileName,
			})
			if err != nil {
				common.Loger.Error("%v", err)
				return
			}
		}(idInt64, complete)
		resp.RespOk(c)
		return
	} else if cmd == "fail" {
		id := c.Query("id")
		idInt64, _ := strconv.ParseInt(id, 10, 64)
		if idInt64 == 0 {
			resp.RespParamErr(c)
			return
		}
		needId := c.Query("need_id")
		if needId == "" {
			resp.RespParamErr(c)
			return
		}
		_, err := calculate_server.CalculateUpdateFail(c, &proto.CalculateCutpartQueue{Id: idInt64, NeedId: needId})
		if err != nil {
			resp.RespInternalErr(c, err.Error())
			return
		}
		resp.RespOk(c)
		return
	} else if cmd == "production_material" {
		client := s.Redis.Get()
		data, err := client.RPop(s.Redis.GetProductionMaterialConsume()).Result()
		if err != nil {
			if err == redis.Nil {
				resp.RespOk(c)
				return
			}
			resp.RespInternalErr(c, err.Error())
			return
		}
		v := order_server_proto.ProductionMaterial{}
		_ = json.Unmarshal([]byte(data), &v)
		//获取计算之后的边缘点坐标
		item := base_server.VtInfo{}
		res, e := calculate_server.GetBorderNode(context.Background(), &calculate_server_proto.GetBorderNodeReq{ObjUrl: v.ObjPath})
		if e != nil {
			resp.RespInternalErr(c, e.Error())
			return
		}
		err = json.Unmarshal([]byte(res.Stdout), &item)
		if err != nil {
			resp.RespInternalErr(c, err.Error())
			return
		}
		item.Code = v.Code
		//item.DxfInfo = item
		bucket, err := oss.GetClient().Bucket("jihe-file")
		if err != nil {
			resp.RespInternalErr(c, err.Error())
			return
		}
		jsonByte, err := json.Marshal(item)
		if err != nil {
			resp.RespInternalErr(c, err.Error())
			return
		}
		fileName := fmt.Sprintf("border_points_%s/%s/%s.json", common.Configer.Runmode, v.OrderId, v.Code)
		err = bucket.PutObject(fileName, bytes.NewReader(jsonByte))
		if err != nil {
			resp.RespInternalErr(c, err.Error())
			return
		}
		resp.RespOk(c, data)
		return
	} else if cmd == "production_material_complete" {
		req := order_server_proto.OrderProductionMaterial{}
		err := c.BindJSON(&req)
		if err != nil {
			resp.RespParamErr(c)
			return
		}
		ret, err := order_server.OrderCreateProductionMaterial(c, &req)
		if err != nil {
			resp.RespInternalErr(c, err.Error())
			return
		}
		resp.RespOk(c, ret)
		return
	} else if cmd == "production_material_fail" {
		orderId := c.Query("order_id")
		if orderId == "" {
			resp.RespParamErr(c)
			return
		}
		_, err := order_server.OrderUpdateProductionMaterialStatus(c, &order_server_proto.OrderUpdateProductionMaterialStatusReq{
			OrderId: orderId,
			Status:  2,
		})
		if err != nil {
			resp.RespInternalErr(c, err.Error())
			return
		}
		resp.RespOk(c)
		return
	} else if cmd == "composing_material" {
		client := s.Redis.Get()
		ret := make([]order_server_proto.ComposingMaterialData, 0)
		data, err := client.RPop(s.Redis.GetComposingMaterialConsume()).Bytes()
		if err != nil {
			if err == redis.Nil {
				resp.RespOk(c, ret)
				return
			}
			resp.RespInternalErr(c, err.Error())
			return
		}
		err = json.Unmarshal(data, &ret)
		if err != nil {
			resp.RespInternalErr(c, err.Error())
			return
		}
		resp.RespOk(c, ret)
		return
	} else if cmd == "composing_material_fail" {
		orderId := c.Query("order_id")
		if orderId == "" {
			resp.RespParamErr(c)
			return
		}
		_, err := order_server.OrderUpdateComposingStatus(c, &order_server_proto.OrderOrder{Id: orderId, ComposingStatus: 3})
		if err != nil {
			resp.RespInternalErr(c, err.Error())
			return
		}
		resp.RespOk(c)
		return
	} else if cmd == "composing_material_complete" {
		orderId := c.Query("order_id")
		if orderId == "" {
			resp.RespParamErr(c)
			return
		}
		req := make([]order_server_proto.ComposingMaterialDataRetItem, 0)
		err := c.BindJSON(&req)
		if err != nil {
			resp.RespInternalErr(c, err.Error())
			return
		}
		urls := make([]string, 0)
		for _, v := range req {
			v.Url = strings.Replace(v.Url, "files.dizanz", "jihe-file.oss-cn-beijing-internal.aliyuncs", -1)
			urls = append(urls, v.Url)
		}
		go func() {
			zipUrl, err := tool.UploadImgAndGetCpZip(urls)
			if err != nil {
				common.Loger.Error("%v", err)
				return
			}
			_, err = order_server.OrderUpdateComposingZip(c, &order_server_proto.OrderOrder{Id: orderId, ComposingZip: zipUrl})
			if err != nil {
				common.Loger.Error("%v", err)
				return
			}
		}()
		resp.RespOk(c)
		return
	}
	resp.RespGeneralErr(c, "无法识别的命令")
}

func (s *CalculateController) GetLineup(c *gin.Context) {
	minId := c.Query("min_id")
	minId64, _ := strconv.ParseInt(minId, 10, 64)
	if minId64 == 0 {
		resp.RespParamErr(c)
		return
	}
	ret, err := calculate_server.CalculateLineupNumByMinId(c, &proto.CalculateLineupNumByMinIdReq{MiniId: minId64})
	if err != nil {
		resp.RespInternalErr(c, err.Error())
		return
	}
	resp.RespOk(c, ret)
}

func (s *CalculateController) Abort(c *gin.Context) {
	needId := c.Query("need_id")
	if needId == "" {
		resp.RespParamErr(c)
		return
	}
	_, err := calculate_server.CalculateAbort(c, &proto.CalculateCutpartQueue{NeedId: needId})
	if err != nil {
		resp.RespInternalErr(c, err.Error())
		return
	}
	resp.RespOk(c)
}

func (s *CalculateController) DoAfresh(c *gin.Context) {
	needId := c.Query("need_id")
	if needId == "" {
		resp.RespParamErr(c)
		return
	}
	needDetail, err := base_server.BaseGetCustomerRegistrationDetail(c, &base_server_proto.BaseCustomerRegistration{Id: needId})
	if err != nil {
		resp.RespInternalErr(c, "debug2"+err.Error())
		return
	}
	//先检测款式是否已下架
	fashionDetail, err := base_server.BaseGetFashionDetail(c, &base_server_proto.BaseGetFashionDetailReq{Id: needDetail.FashionId})
	if err != nil {
		resp.RespInternalErr(c, "debug1"+err.Error())
		return
	}
	if fashionDetail.Fashion.Status != 2 {
		resp.RespGeneralErr(c, "款式已下架")
		return
	}

	qs, err := calculate_server.CalculateGetCutpartQueueByNeedId(c, &proto.CalculateCutpartQueue{NeedId: needId})
	if err != nil {
		resp.RespInternalErr(c, "debug"+err.Error())
		return
	}
	isProgress := false
	for _, v := range qs.List {
		//如果有计算中和待计算的，不能重新计算
		if v.Status == 1 || v.Status == 2 {
			isProgress = true
			break
		}
	}
	if isProgress {
		resp.RespGeneralErr(c, "暂时不能重新计算")
		return
	}
	//清除掉计算队列里的裁片
	var g errgroup.Group
	g.Go(func() error {
		_, err = calculate_server.CalculateDeleteByNeedId(c, &proto.CalculateCutpartQueue{NeedId: needId})
		if err != nil {
			return err
		}
		return nil
	})
	//清除已算好的裁片
	g.Go(func() error {
		_, err = base_server.BaseDeleteNeedCutpartByNeedId(c, &base_server_proto.BaseNeedCutpart{NeedId: needId})
		if err != nil {
			return err
		}
		return nil
	})
	//清除已算吗的obj
	g.Go(func() error {
		_, err = base_server.BaseUpdateNeedObjByNeedId(c, &base_server_proto.BaseCustomerRegistration{Id: needId})
		if err != nil {
			return err
		}
		return nil
	})
	if err = g.Wait(); err != nil {
		resp.RespInternalErr(c, "debug6"+err.Error())
		return
	}
	//预处理
	err = services.PreCalculate(c, needId, "admin")
	if err != nil {
		resp.RespInternalErr(c, "debug7"+err.Error())
		return
	}
	resp.RespOk(c)
}
